package wx

import (
	"crypto/sha1"
	"encoding/json"
	"errors"
	"fmt"
	emlogrus "github.com/go-emix/emix-logrus"
	"io"
	"net/http"
	"service/common/util"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	// 网页授权
	WebOauth2Api = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" +
		AppId + "&secret=" + Secret + "&code={{code}}&grant_type=authorization_code"
	WebRefreshTokenApi = "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
		"appid=" + AppId + "&grant_type=refresh_token&refresh_token="
	WebUserInfoApi = "https://api.weixin.qq.com/sns/userinfo?access_token=" +
		"{{token}}&openid={{openid}}&lang=zh_CN?"
	JsTicketApi = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={{token}}&type=jsapi"
)

type OauthResp struct {
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	Openid       string
	RefreshToken string `json:"refresh_token"`
	Errcode      int
	Errmsg       string
}

func WebOauth(code string) (*OauthResp, error) {
	r := strings.ReplaceAll(WebOauth2Api, "{{code}}",
		code)
	get, err := http.Get(r)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = get.Body.Close()
	}()
	all, err := io.ReadAll(get.Body)
	if err != nil {
		return nil, err
	}
	var or OauthResp
	err = json.Unmarshal(all, &or)
	if err != nil {
		return nil, err
	}
	if or.Errcode != 0 {
		return nil, errors.New(or.Errmsg)
	}
	return &or, nil
}

type UserInfo struct {
	Openid     string
	Nickname   string
	Sex        int
	Headimgurl string
	Errcode    int
	Errmsg     string
}

func GetUserInfo(token, openid string) (*UserInfo, error) {
	r1 := strings.ReplaceAll(WebUserInfoApi, "{{openid}}",
		openid)
	r2 := strings.ReplaceAll(r1, "{{token}}",
		token)
	get, err := http.Get(r2)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = get.Body.Close()
	}()
	all, err := io.ReadAll(get.Body)
	if err != nil {
		return nil, err
	}
	var or UserInfo
	err = json.Unmarshal(all, &or)
	if err != nil {
		return nil, err
	}
	if or.Errcode != 0 {
		return nil, errors.New(or.Errmsg)
	}
	return &or, nil
}

var JsTicket = ""

type JsTicketResp struct {
	Ticket    string
	Errcode   int
	Errmsg    string
	ExpiresIn int `json:"expires_in"`
}

func refreshJsTicket() {
	r := strings.ReplaceAll(JsTicketApi, "{{token}}",
		AccessToken)
	get, err := http.Get(r)
	if err != nil {
		emlogrus.Error("刷新JsTicket错误：", err)
		return
	}
	defer func() {
		_ = get.Body.Close()
	}()
	all, err := io.ReadAll(get.Body)
	if err != nil {
		emlogrus.Error("刷新JsTicket错误：", err)
		return
	}
	var or JsTicketResp
	err = json.Unmarshal(all, &or)
	if err != nil {
		emlogrus.Error("刷新JsTicket错误：", err)
		return
	}
	if or.Errcode != 0 {
		emlogrus.Error("刷新JsTicket错误：", or.Errcode, ",", or.Errmsg)
		return
	}
	JsTicket = or.Ticket
	emlogrus.Info("jsTicket expiresIn", or.ExpiresIn)
	time.AfterFunc(time.Duration(or.ExpiresIn-4*60)*time.Second, func() {
		refreshJsTicket()
	})
}

type JsConfig struct {
	Noncestr  string
	Timestamp int64
	Signature string
}

func SdkConfig(ur string) JsConfig {
	m := make(map[string]string)
	str := util.GenRandStr(8)
	m["noncestr"] = str
	m["jsapi_ticket"] = JsTicket
	var ts = time.Now().Unix()
	fi := strconv.FormatInt(ts, 10)
	m["timestamp"] = fi
	m["url"] = ur
	sign := genSign(m)
	var jc JsConfig
	jc.Noncestr = str
	jc.Signature = sign
	jc.Timestamp = ts
	return jc
}

// 排序字段，sha1签名
func genSign(param map[string]string) string {
	pns := make([]string, 0)
	for k := range param {
		pns = append(pns, k)
	}
	sort.Strings(pns)
	bd := strings.Builder{}
	ln := len(param)
	i := 0
	for _, k := range pns {
		i++
		v := param[k]
		if v == "" {
			continue
		}
		if i == ln {
			bd.WriteString(k + "=" + v)
		} else {
			bd.WriteString(k + "=" + v + "&")
		}
	}
	bs := []byte(bd.String())
	return fmt.Sprintf("%x", sha1.Sum(bs))
}
